import processing.core.*; 
import processing.xml.*; 

import java.applet.*; 
import java.awt.Dimension; 
import java.awt.Frame; 
import java.awt.event.MouseEvent; 
import java.awt.event.KeyEvent; 
import java.awt.event.FocusEvent; 
import java.awt.Image; 
import java.io.*; 
import java.net.*; 
import java.text.*; 
import java.util.*; 
import java.util.zip.*; 
import java.util.regex.*; 

public class imaginaryBackground extends PApplet {

System system;
Particle_group particle_group;
Star_field star_field;

PImage foreground;
//Space_ship space_ship;

public void setup() {
  size(800,200);
  background(0);

  particle_group = new Particle_group(100);
  system = new System(particle_group);

/* @pjs preload="
  data/img.png,
  data/star.png
  "; 
  */  

  star_field = new Star_field();

  float r = random(40,50);
  float g = random(40,50);
  float b = random(40,50);

  star_field.add_field(200, .06f, 1.6f, color(r,g,b));
  star_field.add_field(200, .03f, 1.4f, color(r+10,g+10,b+10));
  star_field.add_field(200, .01f, 1.2f, color(r+20,g+20,b+20));

  foreground = loadImage("data/img.png");

//  space_ship = new Space_ship();

  smooth();
  noStroke();

  rectMode(CENTER);
  shapeMode(CENTER);
  imageMode(CENTER);

  frameRate(60);
}

public void draw() {

  fill(0,0,0,100);
  rect(0+width/2,0+height/2,width,height);

  system.math();
  particle_group.math();
  star_field.math();
//  space_ship.math();

  particle_group.render();
  star_field.render();
  
  image(foreground,width/2,height/2);
//  space_ship.render();

}

public float repulse(float o, float x1, float y1, float x2, float y2, float change) {

  float dp = direction_to(x1, y1, x2, y2);
  if(dp > o) {
    if( dp - o < 360 -dp + o) {
      return change*-1;
    }
    else {
      return change;
    }
  }

  if(dp < o) {
    if(  o - dp < 360-o+dp ) {
      return change;
    }
    else {
      return change*-1;
    }
  }
  return 0;
}


public float distance(float x1,float y1, float x2, float y2) {
  return sqrt(  sq(x2 - x1) + sq(y2 - y1));
}

public float direction_to(float one_x_loc, float one_y_loc, float two_x_loc, float two_y_loc) {
  float a = degrees(atan2(one_y_loc - two_y_loc, one_x_loc- two_x_loc)) +180 ;
  return a;
}


public float attract(float o, float x1, float y1, float x2, float y2, float change) {


  float dp = direction_to(x1, y1, x2, y2);
    if(dp > o) {
    if( dp - o < 360 -dp + o) {
      return change;
    }
    else {
      return change*-1;
    }
  }

  if(dp < o) {
    if(  o - dp < 360-o+dp ) {
      return change*-1;
    }
    else {
      return change;
    }
  }
  return 0;
}
// usefull junk 
/*
class Test {

  collision_detector a;
  particle one;
  particle two;
  gravity g;

  particle_group group;
  system system;

  Test() {

    a = new collision_detector();
    one = new particle();
    two = new particle();
    g = new gravity();

    group = new particle_group(5);
    system = new system(group);
  }

  void gravity() {

    one.mass = 1;
    two.mass = 1;

    g.set_particle_one(one);

    g.set_distance(distance(one.x_loc,one.y_loc,two.x_loc,two.y_loc));
    g.apply_gravity(two);


    println("///////////////");
    println(g.strength);
    println(one.rotation);
    println(two.rotation);

    one.math();
    two.math();

    one.render();
    two.render();
  }

  void collision() {


    one.x_loc = 10;
    one.y_loc = 10;
    two.x_loc = 10;
    two.y_loc = 10;

    a.set_particle_one(one);
    a.is_collision(two);

    println(a.distance);
    println(a.one.radius);
    println(a.collision);
  }

}

*/

////////// the land of the flying raptor and the home of the eye ball ship animation

class Space_ship extends Particle {

  Gravity gravity;

  Collision_detector collision_detector_left;
  Collision_detector collision_detector_right;

  Animation animation_left_eye;
  Animation animation_right_eye;

  Cursor my_cursor;

  Right_eye right_eye;
  Left_eye left_eye;

  Space_ship() {  
    super();

    velocity = 1;
    set_mass(10);

    x_loc = 574;
    y_loc = 78;

    my_cursor = new Cursor();
    left_eye     = new Left_eye(this,75,60,10,10);
    right_eye    = new Right_eye(this,260,120,10,10);//38

    animation_left_eye = new Animation("leftEye",19,91,54);
    animation_right_eye = new Animation("rightEye",19,274,115);

    gravity = new Slopy_hack();
    collision_detector_left = new Collision_detector();
    collision_detector_right = new Collision_detector();

    collision_detector_left.set_particle_one(this);
    collision_detector_left.set_particle_two(left_eye);

    collision_detector_right.set_particle_one(this);
    collision_detector_right.set_particle_two(right_eye);


    gravity.set_particle_one(this);
    gravity.set_particle_two(my_cursor);

    ship_RENDER_setup();
  }

  boolean collision;

  public void math() {

    if((! collision_detector_right.collision) && (!collision_detector_left.collision)) {

      my_cursor.math();

      right_eye.math();
      left_eye.math();

      right_eye.set_collision(collision_detector_right.is_collision());
      left_eye.set_collision(collision_detector_left.is_collision());


      gravity.set_distance(0);
      gravity.aapply_gravity();



      super.math();
    }
  }


  public void render() {
    if((! collision_detector_right.collision) && (!collision_detector_left.collision)) {
      //cursor.render();
      fill(255,0,0);
      ellipse(x_loc,y_loc,mass,mass);
      ship_RENDER();
    }
    else {
      //cursor.render();
      //fill(255,0,100);
      //ellipse(x_loc,y_loc,mass,mass);
    }

    left_eye.render();
    right_eye.render();
  }

  /*
  voidlocation_rotation(){
   direction = direction_to(mouseX,mouseY,player.get_x_loc(),player.get_y_loc())-180;
   x_loc=player.get_x_loc()  + round ( cos(radians(direction))*30 );
   y_loc=player.get_y_loc()  + round ( sin(radians(direction))*30 );
   }
   */

  class Right_eye extends Eye {

    Right_eye(Particle ship,float x_loc, float y_loc, int mass, float dis) {
      super(ship,x_loc,y_loc,mass,dis);
    } 

    public void render() {
      if (collision) {
         animation_right_eye.play();
      }
      else {
        fill(100,100,150);
        ellipse(x_loc, y_loc, mass, mass);
      }
    }
  }

  class Left_eye extends Eye {
   
    Left_eye(Particle ship,float x_loc, float y_loc, int mass, float dis) {
      super(ship,x_loc,y_loc,mass,dis);
    }

    public void render() {
      if (collision) {
       animation_left_eye.play();
      }
      else {
        fill(100,100,150);
        ellipse(x_loc, y_loc, mass, mass);
      }
    }
  }

  class Eye extends Particle {

    boolean collision;

    float dis;
    float center_x;
    float center_y;
    Particle ship;

    Eye(Particle ship,float x_loc, float y_loc, int mass, float dis) {
      center_x=x_loc;
      center_y=y_loc;
      set_mass(mass);
      this.dis = dis;
      this.ship = ship;
    }

    public void set_collision(boolean collision) {
      this.collision = collision;
    }



    public void math() {
      rotation = direction_to(ship.x_loc,ship.y_loc,center_x,center_y)-180;
      x_loc= center_x  +  cos(radians(rotation))*dis;
      y_loc= center_y  +  sin(radians(rotation))*dis;
    }
  }

  class Cursor extends Particle {

    Cursor() {
      super();
      set_mass(5);
      velocity = 0;
      x_loc = width;
      y_loc = height;
    }

    public void math() {
      x_loc = mouseX;
      y_loc = mouseY;
    }
  }

  class Slopy_hack extends Gravity {

    Slopy_hack() {
    }

    public float calculate_strength() {
      strength = 10;

      return strength;
    }
  }

  PImage ship;
  PImage ship1;


  public void ship_RENDER_setup() {
    ship =loadImage("ship.png");
    ship1=loadImage("ship1.png");
  }


  boolean pass;

  public void ship_RENDER() {
    // noFill();
    // ellipseMode(RADIUS);
    //ellipse(x_locOne,y_locOne,r1,r1);
    // fill(255);


    imageMode(CENTER);
    if(pass) {
      image(ship,x_loc,y_loc);
      pass = false;
    }
    else {
      image(ship1,x_loc,y_loc);
      pass = true;
    }
  }
}


///// not even flying raptors can defeat HACK A SOAReS` REX!

//spoon
class Animation {

  String name;
  PImage[] images;
  int number_of_frames;

  boolean play = true;
  int frame=0; 

  float x_loc;
  float y_loc;

  Animation(String name, int number_of_frames, float x_loc, float y_loc) {
    this.name = name;
    this.number_of_frames = number_of_frames;

    images = new PImage[number_of_frames];
    for(int x=0;x < number_of_frames; x++) {
      images[x] = loadImage( name+"/"+(x+1)+".png");
    }

    this.x_loc = x_loc;
    this.y_loc = y_loc;
  }

  public void play() {
    if(play) {
      image(images[frame],x_loc,y_loc);
      frame++;
      if(frame > number_of_frames-1) {
        play = false;
      }
    }
    else {
      image(images[number_of_frames-1],x_loc,y_loc);
    }
  }
}

/// a lot of forward ideas snuck in here for now just calculates the collision state of two objects
/// can be used to link two objects or reused to test multiple different objects



class Collision_detector {

  boolean collision;

  Particle one;
  Particle two;

  float distance;
  
  float[] location = new float[2];

  Collision_detector() {
  } 

  public void set_particle_one(Particle one) {
    this.one = one;
  }

  
  public void set_particle_two(Particle two ) {
   this.two = two;
   }
  
  public float get_distance(){
    collision_distance();
    return distance;    
  }
  

  public boolean is_collision(Particle two) {
    this.two = two; 
    return is_collision();
  }

  public boolean is_collision(Particle one, Particle two) {
    this.one = one;
    this.two = two;
    return is_collision();
  }

/// no I swear the negative makes sense

  public boolean is_collision() {
    
    collision_distance();
    collision = (distance < one.radius+two.radius)?true:false;
    distance = -1;
    return collision;
  }


/// dump all set data
  public void reset() {
    one = null;
    two  = null;
    distance  = -1;
    collision  = false;
    location[0]  = -1;
    location[1]  = -1;
  }
  
  


  public float[] get_location() {
    ////// calculate the point of impact of the last collision
    return location;
  }

  public void collision_distance() {
     distance = sqrt(  sq(two.x_loc - one.x_loc) + sq(two.y_loc - one.y_loc));
  }
}

/// where all the fun stuff happens
/// a lot of forward ideas snuck in here for now just calculates the gravity towards another object

class Gravity {

  Particle one;
  Particle two;

  float gravity;
  float strength;

 
  float distance;

  Gravity() {
  } 

  public void set_particle_one(Particle one) {
    this.one = one;
  }

  public void set_particle_two(Particle two) {
    this.two = two;
  }

  public void set_distance(float distance) {
    this.distance = distance;
  }

  public void apply_gravity(Particle two) {
    this.two = two; 
    aapply_gravity();
  }

  public void apply_gravity(Particle one, Particle two) {
    this.one = one;
    this.two = two;
    aapply_gravity();
  }

  public void aapply_gravity() {
    calculate_strength();
    apply_rotation();
  }

  /// dump all saved data
  public void reset() {
    one = null;
    two  = null;
    strength = 0;
    gravity = 0;
  }

  public void apply_rotation() {
    one.rotation = one.rotation +
      attract(one.rotation,one.x_loc,one.y_loc,
    two.x_loc,two.y_loc,
    strength);
  }


  public float calculate_strength() {
    strength = ((distance*distance)*(one.mass+two.mass))*.00001f;
   
    return strength;
  }

  public float attract(float o, float x1, float y1, float x2, float y2, float change) {
    float direction_to = direction_to(x1, y1, x2, y2);
    if(direction_to > o) {
      if( direction_to - o < 360 -direction_to + o) {
        return change;
      }
      else {
        return change*-1;
      }
    }
    if(direction_to < o) {
      if(  o - direction_to < 360-o+direction_to ) {
        return change*-1;
      }
      else {
        return change;
      }
    }
    return 0;
  }

  public float direction_to(float one_x_loc, float one_y_loc, float two_x_loc, float two_y_loc) {
    float a = degrees(atan2(one_y_loc - two_y_loc, one_x_loc- two_x_loc)) +180 ;
    return a;
  }
}

class Particle {

  float x_loc; 
  float y_loc;


  int mass; ///diameter
  float radius;

  float rotation;
  float velocity;

  boolean alive=true;

  boolean star=false;
  PImage star_image;
  int cc;

 Particle() {
    float offset=0;
    set_mass(1);


    cc =  color(random(230,255),random(240,255),random(230,255));

    x_loc = random(0,width);
    y_loc = random(height-3,height-0);
    rotation = random(0,360);
    star_image = loadImage("data/star.png");
    velocity = .3f;
  }


  public void set_x() {
    x_loc = x_loc + cos(radians(rotation))*velocity ;
  }
  public void set_y() {
    y_loc = y_loc + sin(radians(rotation))*velocity ;
  }


  public void math() {
    if(alive) {

      clamp_to_stage();
      clamp_rotation();

      speed();

      set_x();
      set_y();
    }
  }

  ///// never change mass any other way or it will explode in bad ways !
  public void set_mass(int mass) {
    this.mass = mass;
    radius = (float)mass/2;
  }


  public void speed() {
    if(mass <=4) {
      velocity = .3f;
      star=false;
    }

    if ((mass >4) && (mass<10)) {
      velocity = .2f;
      star = true;
    }
    else {
      if(mass>10) {
        velocity =.1f;
        //space whale = true; its reals
      }
    }
  }

  public void render() {
    if(alive) {
      if(star) {
        pushMatrix();
        image(star_image,x_loc,y_loc,mass+1,mass+1);
        popMatrix();
      }
      else {
        fill(cc);
        rect(x_loc,y_loc,mass,mass);
      }
    }
  }

  public void set_alive(boolean alive) {
    this.alive = alive;
  }

  public boolean get_alive() {
    return alive;
  }

  //// needs to account for objects size
  /// grab the function from the other program that does this the right way
  /* void clamp_to_stage() {
   //if(mass > 5) {
   if(x_loc <0) {
   x_loc = 1;
   
   float hungryRaptor = random(0,180);
   if(hungryRaptor <90) {
   rotation = map(hungryRaptor,0,90,270,360);
   }
   else {
   rotation = map(hungryRaptor,90,180,0,90);
   }
   }
   
   if(x_loc >width) {
   x_loc = width;
   rotation = random(90,270);
   }
   
   if(y_loc <0) {
   y_loc = 1;
   rotation = random(0,180);
   }
   
   if(y_loc >height) {
   y_loc = height-1;
   rotation = random(180,360);
   }
   //  }
   }
   */
  ///erm this is the other function but it was from a really sketchy old sketch but it seems right
  /// I should check it later
  public void clamp_to_stage() {
    if(x_loc<0) {
      x_loc =0+velocity;
      rotation = rotation+(180-(rotation*2));
    }
    else {
      if(x_loc>width) {
        x_loc =width-velocity;
        rotation = rotation+(180-(rotation*2));
      }
    }

    if(y_loc<0) {
      y_loc =0+velocity;
      rotation = rotation-(180-(rotation*2));
    }
    else {
      if(y_loc>height) {
        y_loc =height-velocity;
        rotation = rotation-(180-(rotation*2));
      }
    }
  }



  public void clamp_rotation() {
    if(rotation > 360) {
      rotation = rotation - 360;
    } 
    if(rotation < 0) {
      rotation = rotation + 360;
    }
  }

  public void reset(float x_loc, float y_loc) {
    set_mass(1);
    velocity = .3f;
    rotation = random(0,360);
    this.x_loc = x_loc;
    this.y_loc = y_loc;
  }
}

class Particle_group {

  int number_of_particles; 

  Particle[] particles;

  ArrayList dead_list;


  System system;

  Particle_group(int number_of_particles) {
    particles  = new Particle[number_of_particles];
    dead_list = new ArrayList();

    this.number_of_particles = number_of_particles;

    for(int x=0;x<number_of_particles;x++) {
      particles[x] = new Particle();
    }
  }

  public void render() {
    for(int x=0;x<number_of_particles;x++) {
      particles[x].render();
    }
  }

  public void math() {
    for(int x=0;x<number_of_particles;x++) {
      particles[x].math();
    }
  }

  public boolean kill_particle(Particle particle) {
    if(particle.get_alive()) {
      particle.set_alive(false);
      dead_list.add(particle);
      return true;
    }
    else {
     // println(" ::dead things cant die:: ERROR:PARTICLE_GROUP.KILL_PARTICLE ");
      return false;
    }
  }

  public Particle live_particle(float x_loc, float y_loc) {
    Particle particle;

    particle = (Particle)dead_list.get(0);
    dead_list.remove(0);

    particle.reset(x_loc,y_loc);
    particle.set_alive(true);
    return particle;
  }

  public int dead_list_size() {
    return dead_list.size();
  }
}

class Star_field {

  ArrayList fields_list;
  int number_of_fields;
  
  Star_field() {  
    fields_list = new ArrayList();
  }


  public void add_field(int number_of_stars,float velocity,float radius,int cc) {
    fields_list.add(new Star_group(number_of_stars,velocity,radius,cc));
    number_of_fields++;
  }

  public void math() {
    Star_group star_group;
    for(int x=0;x<number_of_fields;x++) {
      star_group =  (Star_group)fields_list.get(x);
      star_group.math();
    }
  }

  public void render() {

    Star_group star_group;
    for(int x=0;x<number_of_fields;x++) {
      star_group =  (Star_group)fields_list.get(x);
      star_group.render();
    }
  }


  class Star_group {

    Star[] stars;
    int number_of_stars;

    Star_group(int number_of_stars, float velocity,float radius,int cc) {
  
        stars = new Star[number_of_stars]; 
      this.number_of_stars = number_of_stars;
      for(int x=0;x<number_of_stars;x++) {
        stars[x] = new Star(velocity,radius,cc);
      }
    }

    public void math() {
      for(int x=0;x<number_of_stars;x++) {
        stars[x].math();
      }
    }

    public void render() {
      for(int x=0;x<number_of_stars;x++) {
        stars[x].render();
      }
    }


    class Star {
      float x_loc;
      float y_loc;

      float velocity;
      float radius;
      int cc;

      Star(float velocity,float radius,int cc) {
        this.cc = cc;
        this.velocity = velocity;
        this.radius   = radius;
        x_loc = random(0,width);
        y_loc = random(0,height);
      }

      public void math() {

        if(x_loc < -5) {
          x_loc = width+5;
        }

        x_loc = x_loc - velocity;
      }

      public void render() {
        fill(cc);
        rect(x_loc,y_loc,radius,radius);
      }
    }
  }
}


class System {

  Particle_group particle_group;
  Particle[] particles;

  Collision_detector collision_detector;
  Gravity gravity;

  int number_of_particles;




  System(Particle_group particle_group) {
    this.particle_group = particle_group;

    particles = particle_group.particles;
    number_of_particles = particles.length;



    collision_detector = new Collision_detector();
   gravity = new Gravity();
  }

  public void math() {

    //active particle outer for loop object being tested for :x
    //in active particles inner for loop objects being tested against :y

    for(int active=0; active<number_of_particles;active++) {
      for(int passive=0;passive<number_of_particles;passive++) {

        collision_detector.set_particle_one(particles[active]);
        gravity.set_particle_one(particles[active]);

        if((active !=passive) && (particles[passive].alive) && (particles[active].alive)) {         

          if(collision_detector.is_collision(particles[passive])) { 
            if(particles[active].mass >= particles[passive].mass) {

            // explode(particles[active],particles[passive]);
            //  merge(particles[active],particles[passive]);
              //collide(particles[active],particles[passive]);
            }
          }
          else {
            gravity.set_distance(collision_detector.distance);
            gravity.apply_gravity(particles[passive]);
           
          }
        }
      }
      //gravity.reset();
      //collision_detector.reset();
    }
  }


  public void explode(Particle one,Particle two) {
    if( ( one.mass>two.mass) && (one.mass>=6)  ) {
      one.set_mass(one.mass-1);
      particle_group.live_particle(two.x_loc,two.y_loc);
      particle_group.kill_particle(two);
       
      for(int x=0; x < two.mass ; x++) {
        particle_group.live_particle(one.x_loc,one.y_loc);
      }
    }
  }

  public void merge(Particle one,Particle two) {
    if(two.mass <6) {
      one.set_mass(one.mass+1);
      particle_group.kill_particle(two);
    }
  }
}

  static public void main(String args[]) {
    PApplet.main(new String[] { "--bgcolor=#DFDFDF", "imaginaryBackground" });
  }
}
